package com.caoustc.okhttplib.okhttp

import android.text.TextUtils
import com.alibaba.fastjson.JSON
import com.alibaba.fastjson.JSONArray
import com.alibaba.fastjson.JSONObject
import com.caoustc.okhttplib.utils.FileUtils
import com.caoustc.okhttplib.utils.StringUtils
import com.socks.library.KLog
import okhttp3.*
import java.io.File
import java.lang.ref.SoftReference
import java.util.*

class OkHttpRequestParams @JvmOverloads constructor(cycleContext: HttpCycleContext? = null) {
    @kotlin.jvm.JvmField
    val headers: Headers.Builder? = Headers.Builder()
    private val params: MutableList<Part?> = ArrayList()
    private val files: MutableList<Part>? = ArrayList()
    protected var httpCycleContext: SoftReference<HttpCycleContext?>?
    var httpTaskKey: String? = null
        private set
    var requestBody: RequestBody? = null
        get() {
            var body: RequestBody? = null
            if (isApplicationJson) {
                val json: String
                if (paramsIsJsonArray){
                    paramsIsJsonArray = false
                    json = jsonArray?.toJSONString()?:""
                }else{
                    if (jsonParams == null) {
                        val jsonObject = JSONObject()
                        for (part in params) {
                            jsonObject[part?.key] = part?.value
                        }
                        json = jsonObject.toJSONString()
                    } else {
                        json = jsonParams!!.toJSONString()
                    }
                }
                body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json)
            } else if (field != null) {
                body = field
            } else if (files!!.size > 0) {
                var hasData = false
                val builder = MultipartBody.Builder()
                builder.setType(MultipartBody.FORM)
                for (part in params) {
                    val key = part?.key
                    val value = part?.value
                    builder.addFormDataPart(key, value)
                    hasData = true
                }
                for (part in files) {
                    val key = part.key
                    val file = part.fileWrapper
                    if (file != null) {
                        hasData = true
                        builder.addFormDataPart(
                            key,
                            file.fileName,
                            RequestBody.create(file.mediaType, file.file)
                        )
                    }
                }
                if (hasData) {
                    body = builder.build()
                }
            } else {
                val builder = FormBody.Builder()
                for (part in params) {
                    val key = part?.key
                    val value = part?.value
                    builder.add(key, value)
                }
                body = builder.build()
            }
            return body
        }
    var isApplicationJson = false
    var paramsIsJsonArray = false
    var jsonArray:JSONArray? = null
    var isUrlEncoder = false
        private set
    private var jsonParams: JSONObject? = null

    var cacheControl: CacheControl? = null

    private fun init() {
        headers!!.add("charset", "UTF-8")
        val commonParams: List<Part?>? = OkHttpManager.instance?.commonParams
        if (commonParams != null && commonParams.size > 0) {
            params.addAll(commonParams)
        }
        val commonHeaders: Headers? = OkHttpManager.instance?.commonHeaders
        if (commonHeaders != null && commonHeaders.size() > 0) {
            for (i in 0 until commonHeaders.size()) {
                val key = commonHeaders.name(i)
                val value = commonHeaders.value(i)
                headers.add(key, value)
            }
        }
        if (httpCycleContext?.get() != null) {
            httpTaskKey = httpCycleContext?.get()?.httpTaskKey
        }
    }

    fun setHttpCycleContext(cycleContext: HttpCycleContext?) {
        if (cycleContext != null) {
            httpCycleContext = SoftReference(cycleContext)
            if (httpCycleContext!!.get() != null) {
                httpTaskKey = httpCycleContext?.get()?.httpTaskKey
            }
        }
    }

    /**
     * 修改后面加入的parameter key相同时会覆盖前面的参数
     *
     * @param key
     * @param value
     */
    fun addBodyParameter(key: String?, value: String?) {
        val part = Part(key, value)
        if (!StringUtils.isEmpty(key)) {
            if (!params.contains(part)) {
                params.add(part)
            } else {
                params[params.indexOf(part)]?.value = value?:""
            }
        }
    }

    /**
     * 修改后面加入的parameter key相同时不会覆盖前面的参数
     *
     * @param key
     * @param value
     */
    fun addBodyParameterFront(key: String?, value: String?) {

        val part = Part(key, value?:"")
        if (!StringUtils.Companion.isEmpty(key) && !params.contains(part)) {
            params.add(part)
        }
    }

    fun addBodyParameter(key: String?, value: Int) {
        addBodyParameter(key, value.toString())
    }

    fun addBodyParameter(key: String?, value: Long) {
        addBodyParameter(key, value.toString())
    }

    fun addBodyParameter(key: String?, value: Float) {
        addBodyParameter(key, value.toString())
    }

    fun addBodyParameter(key: String?, value: Double) {
        addBodyParameter(key, value.toString())
    }

    fun addBodyParameter(key: String?, value: Boolean) {
        addBodyParameter(key, value.toString())
    }

    fun addBodyParameter(key: String?, file: File?) {
        if (file == null || !file.exists() || file.length() == 0L) {
            return
        }
        val isPng = file.name.lastIndexOf("png") > 0 || file.name.lastIndexOf("PNG") > 0
        if (isPng) {
            addBodyParameter(key, file, "image/png; charset=UTF-8")
            return
        }
        val isJpg =
            file.name.lastIndexOf("jpg") > 0 || file.name.lastIndexOf("JPG") > 0 || file.name.lastIndexOf(
                "jpeg"
            ) > 0 || file.name.lastIndexOf("JPEG") > 0
        if (isJpg) {
            addBodyParameter(key, file, "image/jpeg; charset=UTF-8")
            return
        }
        addBodyParameter(key, FileWrapper(file, FileUtils.Companion.guessMimeType(file.path)))
    }

    fun addBodyParameter(key: String?, file: File?, contentType: String?) {
        if (file == null || !file.exists() || file.length() == 0L) {
            return
        }
        var mediaType: MediaType? = null
        try {
            mediaType = MediaType.parse(contentType)
        } catch (e: Exception) {
            KLog.e("okhttp", e.message)
        }
        addBodyParameter(key, FileWrapper(file, mediaType))
    }

    fun addBodyParameter(key: String?, file: File?, mediaType: MediaType?) {
        if (file == null || !file.exists() || file.length() == 0L) {
            return
        }
        addBodyParameter(key, FileWrapper(file, mediaType))
    }

    fun addFormDataPartFiles(key: String?, files: List<File?>) {
        for (file in files) {
            if (file == null || !file.exists() || file.length() == 0L) {
                continue
            }
            addBodyParameter(key, file)
        }
    }

    fun addBodyParameter(key: String?, files: List<File?>, mediaType: MediaType?) {
        for (file in files) {
            if (file == null || !file.exists() || file.length() == 0L) {
                continue
            }
            addBodyParameter(key, FileWrapper(file, mediaType))
        }
    }

    fun addBodyParameter(key: String?, fileWrapper: FileWrapper?) {
        if (!StringUtils.Companion.isEmpty(key) && fileWrapper != null) {
            val file = fileWrapper.file
            if (!file.exists() || file.length() == 0L) {
                return
            }
            files?.add(Part(key, fileWrapper))
        }
    }

    /**
     * 是否为文件上传.
     * @return
     */
    val isFileUpload: Boolean
        get() = files != null && files.size > 0

    fun addBodyParameter(key: String?, fileWrappers: List<FileWrapper?>) {
        for (fileWrapper in fileWrappers) {
            addBodyParameter(key, fileWrapper)
        }
    }

    fun addFormDataParts(params: List<Part?>?) {
        this.params.addAll(params!!)
    }

    fun addHeader(line: String?) {
        headers!!.add(line)
    }

    fun addHeader(key: String?, value: String?) {
        var value = value
        if (value == null) {
            value = ""
        }
        if (!TextUtils.isEmpty(key)) {
            headers!!.add(key, value)
        }
    }

    fun addHeader(key: String?, value: Int) {
        addHeader(key, value.toString())
    }

    fun addHeader(key: String?, value: Long) {
        addHeader(key, value.toString())
    }

    fun addHeader(key: String?, value: Float) {
        addHeader(key, value.toString())
    }

    fun addHeader(key: String?, value: Double) {
        addHeader(key, value.toString())
    }

    fun addHeader(key: String?, value: Boolean) {
        addHeader(key, value.toString())
    }

    fun urlEncoder() {
        isUrlEncoder = true
    }

    fun clear() {
        params.clear()
        files!!.clear()
    }

    fun convertApplicationJson(key: String, jsonString: String?) {
        isApplicationJson = true
        jsonParams = JSON.parseObject("{$key:$jsonString}")
    }

    fun applicationJson(jsonParams: JSONObject?) {
        isApplicationJson = true
        this.jsonParams = jsonParams
    }

    fun applicationStringJson(jsonString: String?) {
        isApplicationJson = true
        jsonParams = JSONObject.parseObject(jsonString)
    }

    fun applicationJsonArray(jsonArray: JSONArray?) {
        isApplicationJson = true
        paramsIsJsonArray = true
        this.jsonArray = jsonArray
    }

    fun applicationJson() {
        isApplicationJson = true
    }

    private fun setCustomRequestBody(requestBody: RequestBody?) {
        this.requestBody = requestBody
    }

    fun setRequestBodyString(string: String?) {
        setRequestBody(MediaType.parse("text/plain; charset=utf-8"), string)
    }

    fun setRequestBody(mediaType: String?, string: String?) {
        setRequestBody(MediaType.parse(mediaType), string)
    }

    fun setRequestBody(mediaType: MediaType?, string: String?) {
        setCustomRequestBody(RequestBody.create(mediaType, string))
    }

    val formParams: List<Part?>
        get() = params

    /*fun getRequestBody(): RequestBody? {
        var body: RequestBody? = null
        if (isApplicationJson) {
            val json: String
            if (jsonParams == null) {
                val jsonObject = JSONObject()
                for (part in params) {
                    jsonObject[part?.key] = part?.value
                }
                json = jsonObject.toJSONString()
            } else {
                json = jsonParams!!.toJSONString()
            }
            body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json)
        } else if (requestBody != null) {
            body = requestBody
        } else if (files!!.size > 0) {
            var hasData = false
            val builder = MultipartBody.Builder()
            builder.setType(MultipartBody.FORM)
            for (part in params) {
                val key = part?.key
                val value = part?.value
                builder.addFormDataPart(key, value)
                hasData = true
            }
            for (part in files) {
                val key = part.key
                val file = part.fileWrapper
                if (file != null) {
                    hasData = true
                    builder.addFormDataPart(
                        key,
                        file.fileName,
                        RequestBody.create(file.mediaType, file.file)
                    )
                }
            }
            if (hasData) {
                body = builder.build()
            }
        } else {
            val builder = FormBody.Builder()
            for (part in params) {
                val key = part?.key
                val value = part?.value
                builder.add(key, value)
            }
            body = builder.build()
        }
        return body
    }*/

    override fun toString(): String {
        val result = StringBuilder()
        for (part in params) {
            val key = part?.key
            val value = part?.value
            if (result.isNotEmpty()) result.append("&")
            result.append(key)
            result.append("=")
            result.append(value)
        }
        for (part in files!!) {
            val key = part.key
            if (result.isNotEmpty()) result.append("&")
            result.append(key)
            result.append("=")
            result.append("FILE")
        }
        if (jsonParams != null) {
            result.append(jsonParams!!.toJSONString())
        }
        return result.toString()
    }

    init {
        httpCycleContext = SoftReference(cycleContext)
        init()
    }
}